﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using MySql.Data.MySqlClient;
using DateBaseConnectionPool;

namespace TestMySqlConnetPoll
{
    [Serializable]
    public class ObjKGL
    {
        public Int32 u_time;
        public byte type;   //类型，全体或者变化
        public short len;
        public byte[] v = new byte[2000];
    }
    public struct kglstuct
    {
        public int u_time;
        public int Statue;
    }
    public class KGL_Table
    {
        #region  外部函数定义  变量定义，开关量表
        /// <summary>
        /// 从数据库中取到的缓冲区，包含全体和变化开关量
        /// </summary>
        public List<ObjKGL> KGL_ValueList = new List<ObjKGL>();     
        /// <summary>
        /// 把变化开关量放入了全体开关量的缓冲区.
        /// </summary>
        public List<ObjKGL> wholeKGL_ValueList = new List<ObjKGL>();
        public bool Ready_KGL_ValueList = false;
        public int ErrorFlag = 0;

        public struct KglTrheadStruct
        {
            public string TableName;
            public int p_day;
            public int s_time;
            public int e_time;
        }

        public void Read_KGL_ValueList(DateTime p_day)
        {
            ErrorFlag = 0;
            Ready_KGL_ValueList = false;
            KglTrheadStruct obj = new KglTrheadStruct();
            obj.TableName = "KGL_Table";
            obj.p_day = p_day.Day;
            obj.s_time = MySql.DateTimeToTime_t(new DateTime(p_day.Year, p_day.Month, p_day.Day, 0, 0, 0));
            obj.e_time = MySql.DateTimeToTime_t(new DateTime(p_day.Year, p_day.Month, p_day.Day, 23, 59, 59));
            ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadReadKGL_ValueList), obj);
        }

        public void Read_KGL_ValueList(int s_time, int e_time)
        {
            ErrorFlag = 0;
            Ready_KGL_ValueList = false;
            KglTrheadStruct obj = new KglTrheadStruct();
            obj.TableName = "KGL_Table";
            obj.s_time = s_time;
            obj.e_time = e_time;
            ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadReadKGL_ValueList_SE), obj);
        }
        /// <summary>
        /// 获取开关量状态，一般是一天的状态
        /// </summary>
        /// <param name="DevTypeName"></param>
        /// <param name="DevName"></param>
        /// <param name="Attri"></param>
        /// <param name="t"></param>
        /// <returns></returns>
        public bool GetKGLStatue(string DevTypeName,string DevName,string Attri,ref List<kglstuct> t)
        {
            int KglNo = -1;
            if(c_Device.DeviceMsgGather.ContainsKey(DevTypeName))
            {
                int AttriNo = -1;    //属性位置
                for (int i = 0; i < c_Device.DeviceMsgGather[DevTypeName].ArrtiList.Count; i++)
                {
                    if (Attri == c_Device.DeviceMsgGather[DevTypeName].ArrtiList[i].AttriName)
                    {
                        AttriNo = i;
                        break;
                    }
                }
                if (AttriNo != -1)
                {
                    for (int i = 0; i < c_Device.DeviceMsgGather[DevTypeName].DevList.Count; i++)
                    {
                        if (DevName == c_Device.DeviceMsgGather[DevTypeName].DevList[i].DevName)
                        {
                            KglNo = c_Device.DeviceMsgGather[DevTypeName].DevList[i].AnalogSet[AttriNo];
                        }
                    }
                }
            }
            if (KglNo != -1)
            {
                kglstuct obj = new kglstuct();
                for (int i = 0; i < wholeKGL_ValueList.Count; i++)
                {
                    obj.u_time = wholeKGL_ValueList[i].u_time;
                    int offset = KglNo / 8; //所在字节数
                    if (offset >= wholeKGL_ValueList[i].len) return false;
                    int b = offset % 8;     //所在字节的位置
                    byte k = wholeKGL_ValueList[i].v[offset];
                    obj.Statue = (k >> b) & 0x01;
                    t.Add(obj);
                }
                return true;
            }
            return false;
        }

        #endregion
        private Mutex keyMutex = new Mutex();
        static private int keykey = 0;

        /// <summary>
        /// 把从数据库取到的开关量数据，把变化开关量整理到全体开关量里面去.
        /// </summary>
        private void DisposeWholeKglData()
        {
            wholeKGL_ValueList.Clear();
            ObjKGL tclone = new ObjKGL();
            foreach (ObjKGL key in KGL_ValueList)
            {
                if (key.type == 0x01) //全体开关量
                {
                    wholeKGL_ValueList.Add(key);
                    ///全体开关量赋值.
                    tclone = new ObjKGL();
                    tclone.u_time = key.u_time;
                    tclone.len = key.len;
                    tclone.type = 0x01;
                    key.v.CopyTo(tclone.v, 0);
                }
                else
                {                    
                    int j = 0;
                    for (int i = 0; i < (key.len/6); i++)
                    {
                        int ttime = key.v[j] + (key.v[j + 1] << 8) + (key.v[j + 2] << 16) + (key.v[j + 3] << 24);
                        j += 4;
                        int no = key.v[j] + (key.v[j + 1] << 8);
                        j += 2;
                        byte s = (byte)(no >> 15);
                        no = no & 0x7fff;
                        int B = no / 8;     //字节数
                        int b = no % 8;     //字节所在的位
                        tclone.u_time = ttime;                        
                        if (B < tclone.v.Length)
                        {
                            byte v = tclone.v[B];
                            v = (byte)(v & (s << b));
                            v = (byte)(v | (s << b));
                            tclone.v[B] = v;
                        }
                        wholeKGL_ValueList.Add(tclone);
                        ObjKGL ttc = new ObjKGL();
                        ttc = c_NetData.Clone(tclone) as ObjKGL;
                        //tclone = ttc;   //重新复制一份全体开关量，用于下一个变化数据的原始数据.
                        tclone = new ObjKGL();
                        tclone = c_NetData.Clone(ttc) as ObjKGL;
                    }
                }
            }
        }


        #region 开关量表
        /// <summary>
        /// 写开关量表
        /// </summary>
        /// <param name="TableName"></param>
        /// <param name="p_day"></param>
        /// <param name="u_time"></param>
        /// <param name="type"></param>
        /// <param name="values"></param>
        /// <returns></returns>
        private bool InsertKGL_Table(string TableName, byte p_day, int u_time, short len, byte[] values)
        {
            bool tmpFlag = false;
            if (TableName == "") return tmpFlag;
            if (MySql.c != null && MySql.c.State == PoolState.Run)
            {
                MySqlConnection con = new MySqlConnection();
                string key = TableName + DateTime.Now.Second.ToString() + DateTime.Now.Millisecond.ToString() + Interlocked.Increment(ref keykey).ToString();
                try
                {
                    con = (MySqlConnection)MySql.c.GetConnectionFormPool(key);
                }
                catch (MySqlException ex)
                {
                    //写异常日志
                    tmpFlag = false;
                }
                
                if (con != null)
                {
                    StringBuilder InsertStatement = new StringBuilder();

                    InsertStatement.Append(string.Format("insert into {0}(p_day,u_time,len,value)", TableName));

                    InsertStatement.Append("values(?p_day,?u_time,?len,?value)");

                    MySqlCommand InsertCommand = new MySqlCommand(InsertStatement.ToString(), con);
                    InsertCommand.Parameters.Add(new MySqlParameter("?p_day", MySqlDbType.Byte)).Value = p_day;
                    InsertCommand.Parameters.Add(new MySqlParameter("?u_time", MySqlDbType.UInt32)).Value = u_time;
                    InsertCommand.Parameters.Add(new MySqlParameter("?len", MySqlDbType.Int16)).Value = len;
                    InsertCommand.Parameters.Add(new MySqlParameter("?value", MySqlDbType.VarBinary)).Value = values.ToArray();

                    try
                    {
                        int k = InsertCommand.ExecuteNonQuery();
                        if (k == 1) tmpFlag = true;        //写入成功
                        else tmpFlag = false;              //写入失败
                    }
                    catch (MySqlException e)
                    {
                        //写异常日记
                        tmpFlag = false;       //写入失败
                    }
                    try
                    {
                        MySql.c.DisposeConnection(key);
                    }
                    catch (MySqlException e)
                    {
                        //写异常日记
                    }
                }
            }
            return tmpFlag;
        }

        private void AddToKGL_ValueList(int u_time, short len, byte type, byte[] values)
        {
            ObjKGL tmp = new ObjKGL();
            tmp.u_time = u_time;
            tmp.type = type;
            tmp.len = len;
            tmp.v = values;
            KGL_ValueList.Add(tmp);
        }

        /// <summary>
        /// 取一天所有的开关量，也可以指定类型，取一天某种类型的所有开关量
        /// </summary>
        /// <param name="TableName"></param>
        /// <param name="p_day"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        private void ThreadReadKGL_ValueList(object o)
        {
            KglTrheadStruct obj = (KglTrheadStruct)o;
            string TableName = obj.TableName;
            byte p_day = (byte)obj.p_day;
            KGL_ValueList.Clear();                             //没有指定设备类型，则清除该缓冲区.
            if (TableName == "") return;
            if (MySql.c != null && MySql.c.State == PoolState.Run)
            {
                MySqlConnection con = new MySqlConnection();
                string key = TableName + DateTime.Now.Second.ToString() + DateTime.Now.Millisecond.ToString() + Interlocked.Increment(ref keykey).ToString();
                try
                {
                    con = (MySqlConnection)MySql.c.GetConnectionFormPool(key);
                }
                catch (MySqlException ex)
                {
                    //写异常日志
                    ErrorFlag = 1;
                }
                if (con != null)
                {
                    StringBuilder SelectStatement = new StringBuilder();

                    SelectStatement.Append(string.Format("select u_time,type,len,value from {0} where p_day={1}", TableName, p_day));
                    SelectStatement.Append(string.Format(" and u_time >= {0} and u_time <= {1}", obj.s_time, obj.e_time));

                    try
                    {
                        MySqlCommand selectCommand = new MySqlCommand(SelectStatement.ToString(), con);
                        selectCommand.CommandTimeout = 120;   //120s操作超时

                        MySqlDataReader read = selectCommand.ExecuteReader();
                        while (read.Read())
                        {
                            int u_time = read.GetInt32(read.GetOrdinal("u_time"));
                            byte type = read.GetByte(read.GetOrdinal("type"));
                            short l = read.GetInt16(read.GetOrdinal("len"));
                            byte[] v = new byte[2000];
                            read.GetBytes(read.GetOrdinal("value"), 0, v, 0, l);
                            AddToKGL_ValueList(u_time, l, type, v);
                        }
                        read.Close();
                        DisposeWholeKglData();  //变化开关量并入全体开关量
                        Ready_KGL_ValueList = true;
                    }
                    catch (MySqlException e)
                    {
                        ErrorFlag = 2;
                    }
                    try
                    {
                        MySql.c.DisposeConnection(key);
                    }
                    catch (MySqlException e)
                    {
                        ErrorFlag = 3;
                    }
                }
                else ErrorFlag = 5;
            }
            else ErrorFlag = 4;
        }

        /// <summary>
        /// 取一段时间内的开关量,也可以指定类型，取某一个时刻某种类型的开关量
        /// 需要考虑s_time 和e_time 不在同一天内.
        /// </summary>
        /// <param name="TableName"></param>
        /// <param name="s_time"></param>
        /// <param name="e_time"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        private void ThreadReadKGL_ValueList_SE(object o)
        {
            KglTrheadStruct obj = (KglTrheadStruct)o;

            string TableName = obj.TableName;
            int s_time = obj.s_time;
            int e_time = obj.e_time;
            KGL_ValueList.Clear();        //没有指定设备类型，则清除该缓冲区.
            if (TableName == "")
            {
                ErrorFlag = 1;
                return;
            }

            bool OneDay = true;         //开始时间和结束时间是否在同一天内
            int p_day = 0;             //第一天的天
            int sencond_day = 0;       //第二天的天

            DateTime st = MySql.Time_tToDateTime(s_time);
            DateTime et = MySql.Time_tToDateTime(e_time);

            if (st.Day == et.Day)
            {
                OneDay = true;
                p_day = st.Day;
            }
            else
            {
                OneDay = false;
                p_day = st.Day;
                sencond_day = et.Day;
            }


            if (MySql.c != null && MySql.c.State == PoolState.Run)
            {
                MySqlConnection con = new MySqlConnection();
                string key = TableName + DateTime.Now.Second.ToString() + DateTime.Now.Millisecond.ToString() + Interlocked.Increment(ref keykey).ToString();
                try
                {
                    con = (MySqlConnection)MySql.c.GetConnectionFormPool(key);
                }
                catch (MySqlException ex)
                {
                    //写异常日志
                    ErrorFlag = 2;
                }
                if (con != null)
                {
                    StringBuilder SelectStatement = new StringBuilder();
                    if (OneDay) //在一天内
                    {
                        SelectStatement.Append(string.Format("select u_time, type,len,value from {0} where p_day={1} and u_time>={2} and u_time<={3} order by u_time", TableName, p_day, s_time, e_time));
                    }
                    else   //跨分区查询
                    {
                        SelectStatement.Append(string.Format("select u_time, type,len,value from {0} where (p_day={1} or p_day ={2}) and u_time>={3} and u_time<={4} order by u_time", TableName, p_day, sencond_day, s_time, e_time));
                    }
                    try
                    {
                        MySqlCommand selectCommand = new MySqlCommand(SelectStatement.ToString(), con);
                        selectCommand.CommandTimeout = 120;   //120s操作超时

                        MySqlDataReader read = selectCommand.ExecuteReader();
                        while (read.Read())
                        {
                            int u_time = read.GetInt32(read.GetOrdinal("u_time"));
                            byte type = read.GetByte(read.GetOrdinal("type"));
                            short l = read.GetInt16(read.GetOrdinal("len"));
                            byte[] v = new byte[2000];
                            read.GetBytes(read.GetOrdinal("value"), 0, v, 0, l);
                            AddToKGL_ValueList(u_time, l, type, v);
                        }
                        read.Close();
                        DisposeWholeKglData();  //变化开关量并入全体开关量
                        Ready_KGL_ValueList = true;
                    }
                    catch (MySqlException e)
                    {
                        ErrorFlag = 3;
                    }

                    try
                    {
                        MySql.c.DisposeConnection(key);
                    }
                    catch (MySqlException e)
                    {
                        ErrorFlag = 4;
                    }
                }
                else ErrorFlag = 5;
            }
            else ErrorFlag = 6;
        }
        #endregion
    }
}
